const request = require('supertest');
const express = require('express');
const fundsRoutes = require('../../routes/funds');

// mock 数据库
const mockQuery = jest.fn();
jest.mock('../../config/database', () => ({
  query: (...args) => mockQuery(...args)
}));

// mock 认证
jest.mock('../../middleware/auth', () => ({
  authenticateToken: (req, res, next) => { req.user = { id: 1, role: 'admin' }; next(); },
  checkRole: () => (req, res, next) => next()
}));

describe('Funds Routes', () => {
  let app;
  beforeEach(() => {
    app = express();
    app.use(express.json());
    app.use('/funds', fundsRoutes);
    mockQuery.mockReset();
  });

  test('GET /funds 返回基金列表', async () => {
    mockQuery.mockResolvedValueOnce([{ total: 1 }]); // countResult
    mockQuery.mockResolvedValueOnce([{ id: 1, name: '测试基金', tags: '[]' }]); // funds
    const res = await request(app).get('/funds');
    expect(res.status).toBe(200);
    expect(Array.isArray(res.body.data)).toBe(true);
    expect(res.body.data[0].name).toBe('测试基金');
  });

  test('GET /funds/:id 返回单个基金', async () => {
    mockQuery.mockResolvedValueOnce([{ id: 1, name: '测试基金', tags: '[]' }]); // fund
    mockQuery.mockResolvedValueOnce([]); // navHistory
    mockQuery.mockResolvedValueOnce([]); // holdings
    const res = await request(app).get('/funds/1');
    expect(res.status).toBe(200);
    expect(res.body.fund.id).toBe(1);
  });

  test('GET /funds/:id 返回404', async () => {
    mockQuery.mockResolvedValueOnce([]); // fund
    const res = await request(app).get('/funds/999');
    expect(res.status).toBe(404);
  });

  test('GET /funds 错误处理', async () => {
    mockQuery.mockRejectedValueOnce(new Error('db error'));
    const res = await request(app).get('/funds');
    expect(res.status).toBe(500);
  });

  test('POST /funds 新增基金', async () => {
    mockQuery.mockResolvedValueOnce({ insertId: 2 });
    const res = await request(app).post('/funds').send({ name: '新基金' });
    expect(res.status).toBe(201);
  });

  test('PUT /funds/:id 更新基金', async () => {
    mockQuery.mockResolvedValueOnce({ affectedRows: 1 });
    const res = await request(app).put('/funds/1').send({ name: '更新基金' });
    expect(res.status).toBe(200);
  });

  test('DELETE /funds/:id 删除基金', async () => {
    mockQuery.mockResolvedValueOnce({ affectedRows: 1 });
    const res = await request(app).delete('/funds/1');
    expect(res.status).toBe(200);
  });

  test('POST /funds 缺少参数', async () => {
    const res = await request(app).post('/funds').send({});
    expect(res.status).toBe(400);
  });

  test('PUT /funds/:id 更新不存在的基金', async () => {
    mockQuery.mockResolvedValueOnce({ affectedRows: 0 });
    const res = await request(app).put('/funds/999').send({ name: '不存在' });
    expect(res.status).toBe(404);
  });

  test('DELETE /funds/:id 删除不存在的基金', async () => {
    mockQuery.mockResolvedValueOnce({ affectedRows: 0 });
    const res = await request(app).delete('/funds/999');
    expect(res.status).toBe(404);
  });

  test('POST /funds 数据库异常', async () => {
    mockQuery.mockRejectedValueOnce(new Error('db error'));
    const res = await request(app).post('/funds').send({ name: '异常' });
    expect(res.status).toBe(500);
  });

  test('PUT /funds/:id 数据库异常', async () => {
    mockQuery.mockRejectedValueOnce(new Error('db error'));
    const res = await request(app).put('/funds/1').send({ name: '异常' });
    expect(res.status).toBe(500);
  });

  test('DELETE /funds/:id 数据库异常', async () => {
    mockQuery.mockRejectedValueOnce(new Error('db error'));
    const res = await request(app).delete('/funds/1');
    expect(res.status).toBe(500);
  });

  test('GET /funds/stats 返回统计信息', async () => {
    mockQuery.mockResolvedValueOnce([
      {
        total_funds: 10,
        avg_yearly_return: 8.5,
        avg_max_drawdown: 3.2,
        avg_total_assets: 10000
      }
    ]);
    const res = await request(app).get('/funds/stats');
    expect(res.status).toBe(200);
    expect(res.body.totalFunds).toBe(10);
    expect(res.body.avgYearlyReturn).toBe('8.50');
  });

  test('GET /funds/stats 数据库异常', async () => {
    mockQuery.mockRejectedValueOnce(new Error('db error'));
    const res = await request(app).get('/funds/stats');
    expect(res.status).toBe(500);
  });

  test('GET /funds/companies/list 返回公司列表', async () => {
    mockQuery.mockResolvedValueOnce([{ id: 1, name: '公司A' }]);
    const res = await request(app).get('/funds/companies/list');
    expect(res.status).toBe(200);
    expect(res.body.data[0].name).toBe('公司A');
  });

  test('GET /funds/companies/list 数据库异常', async () => {
    mockQuery.mockRejectedValueOnce(new Error('db error'));
    const res = await request(app).get('/funds/companies/list');
    expect(res.status).toBe(500);
  });

  test('GET /funds/managers/list 返回经理列表', async () => {
    mockQuery.mockResolvedValueOnce([{ id: 1, name: '经理A' }]);
    const res = await request(app).get('/funds/managers/list');
    expect(res.status).toBe(200);
    expect(res.body.data[0].name).toBe('经理A');
  });

  test('GET /funds/managers/list 数据库异常', async () => {
    mockQuery.mockRejectedValueOnce(new Error('db error'));
    const res = await request(app).get('/funds/managers/list');
    expect(res.status).toBe(500);
  });

  test('GET /funds/tags/list 返回标签列表', async () => {
    mockQuery.mockResolvedValueOnce([{ tag: '科技' }, { tag: '消费' }]);
    const res = await request(app).get('/funds/tags/list');
    expect(res.status).toBe(200);
    expect(res.body.data).toContain('科技');
  });

  test('GET /funds/tags/list 数据库异常', async () => {
    mockQuery.mockRejectedValueOnce(new Error('db error'));
    const res = await request(app).get('/funds/tags/list');
    expect(res.status).toBe(500);
  });

  test('GET /funds/filter/options 返回筛选选项', async () => {
    mockQuery
      .mockResolvedValueOnce([{ fund_type: '股票型' }])
      .mockResolvedValueOnce([{ category: '混合型' }])
      .mockResolvedValueOnce([{ risk_level: '中风险' }])
      .mockResolvedValueOnce([{ company_name: '公司A' }]);
    const res = await request(app).get('/funds/filter/options');
    expect(res.status).toBe(200);
    expect(res.body.fundTypes).toContain('股票型');
    expect(res.body.categories).toContain('混合型');
    expect(res.body.riskLevels).toContain('中风险');
    expect(res.body.companies).toContain('公司A');
  });

  test('GET /funds/filter/options 数据库异常', async () => {
    mockQuery.mockRejectedValueOnce(new Error('db error'));
    const res = await request(app).get('/funds/filter/options');
    expect(res.status).toBe(500);
  });

  test('POST /funds/portfolio 保存基金组合', async () => {
    mockQuery.mockResolvedValueOnce({ insertId: 1 }); // 插入组合
    mockQuery.mockResolvedValue({}); // 插入基金
    const res = await request(app).post('/funds/portfolio').send({ name: '组合A', description: '', funds: [{ id: 1 }, { id: 2 }] });
    expect(res.status).toBe(201);
    expect(res.body.portfolioId).toBe(1);
  });

  test('POST /funds/portfolio 数据库异常', async () => {
    mockQuery.mockRejectedValueOnce(new Error('db error'));
    const res = await request(app).post('/funds/portfolio').send({ name: '组合A', description: '', funds: [{ id: 1 }] });
    expect(res.status).toBe(500);
  });

  test('GET /funds/portfolio/user 获取用户基金组合', async () => {
    mockQuery.mockResolvedValueOnce([{ id: 1, name: '组合A', fund_count: 2 }]);
    const res = await request(app).get('/funds/portfolio/user');
    expect(res.status).toBe(200);
    expect(res.body.data[0].name).toBe('组合A');
  });

  test('GET /funds/portfolio/user 数据库异常', async () => {
    mockQuery.mockRejectedValueOnce(new Error('db error'));
    const res = await request(app).get('/funds/portfolio/user');
    expect(res.status).toBe(500);
  });

  test('GET /funds/portfolio/:id 获取组合详情', async () => {
    mockQuery.mockResolvedValueOnce([{ id: 1, name: '组合A', user_id: 1 }]); // portfolio
    mockQuery.mockResolvedValueOnce([{ id: 1, tags: '[]', weight: 0.5 }]); // funds
    const res = await request(app).get('/funds/portfolio/1');
    expect(res.status).toBe(200);
    expect(res.body.portfolio.id).toBe(1);
  });

  test('GET /funds/portfolio/:id 组合不存在', async () => {
    mockQuery.mockResolvedValueOnce([]); // portfolio
    const res = await request(app).get('/funds/portfolio/999');
    expect(res.status).toBe(404);
  });

  test('GET /funds/portfolio/:id 数据库异常', async () => {
    mockQuery.mockRejectedValueOnce(new Error('db error'));
    const res = await request(app).get('/funds/portfolio/1');
    expect(res.status).toBe(500);
  });

  test('DELETE /funds/portfolio/:id 删除组合', async () => {
    mockQuery.mockResolvedValueOnce([{ id: 1 }]); // portfolio
    mockQuery.mockResolvedValue({}); // 删除基金
    mockQuery.mockResolvedValue({}); // 删除组合
    const res = await request(app).delete('/funds/portfolio/1');
    expect(res.status).toBe(200);
    expect(res.body.message).toBe('组合删除成功');
  });

  test('DELETE /funds/portfolio/:id 组合不存在', async () => {
    mockQuery.mockResolvedValueOnce([]); // portfolio
    const res = await request(app).delete('/funds/portfolio/999');
    expect(res.status).toBe(404);
  });

  test('DELETE /funds/portfolio/:id 数据库异常', async () => {
    mockQuery.mockRejectedValueOnce(new Error('db error'));
    const res = await request(app).delete('/funds/portfolio/1');
    expect(res.status).toBe(500);
  });

  test('GET /funds/:id/profile 返回基金画像', async () => {
    mockQuery.mockResolvedValueOnce([
      {
        performanceRating: '优秀',
        suitableInvestor: '激进型投资者'
      }
    ]);
    const res = await request(app).get('/funds/1/profile');
    expect(res.status).toBe(200);
    expect(res.body.performanceRating).toBe('优秀');
    expect(res.body.suitableInvestor).toBe('激进型投资者');
  });

  test('GET /funds/:id/profile 基金不存在', async () => {
    mockQuery.mockResolvedValueOnce([]);
    const res = await request(app).get('/funds/999/profile');
    expect(res.status).toBe(404);
  });

  test('GET /funds/:id/profile 数据库异常', async () => {
    mockQuery.mockRejectedValueOnce(new Error('db error'));
    const res = await request(app).get('/funds/1/profile');
    expect(res.status).toBe(500);
  });
}); 